import request from '@/request.js'
import utils from '@/utils/index.js'
import config from '@/config.js'
import autotilesOptions from './autotilesOptions.js'

import tilesMap from '@/components/tilesMap/tilesMap.vue'

/*
  on-change 地图信息发生任何改变时,触发事件更新数据
  事件名列表
  open 打开地图
  close 关闭地图
  select 选中地图块
*/

export default {
  name: 'mapEditor',
  props: {
    // 选中的地图
    mapFolder: {
      type: Array,
      default: null
    },
    // 打开的地图，详细信息
    // 编辑器和右边的资源管理器都要访问该属性，所以做双向绑定
    map: {
      type: Object,
      default () {
        return {}
      }
    },
    // 选中的地图ID
    selectedId: {
      type: String,
      default: null
    },
    // 选中的图块数据
    selectedTiles: {
      type: Object,
      default: null
    }
  },
  components: {
    tilesMap
  },
  data () {
    return {
      isLoading: false,
      isSave: true,
      itemDefaultSize: 32,
      
      // 创建地图文件
      createModal: false,
      createLoading: false,
      createForm: {
        // 地图的行数列数
        row: 15,
        col: 15,     
      },
      // 验证
      createValidate: {
        row: [
          { type: 'number', required: true, message: '不得为空', trigger: 'blur' }
        ],
        col: [
          { type: 'number', required: true, message: '不得为空', trigger: 'blur' }
        ]
      },
      
      // 地图详细数据
      openedId: null,
      mapData: null,
      selectedItem: null,
      hoverIndex: null,
      
      // 选中的内容
      selectedObj: this.selectedTiles,
      
      // 复制粘贴
      copyItem: null,
      
      // 地图拖拽编辑
      isDragEnabled: false,
      dragRememberIndex: -1,
      
      // 地图工具
      eraseEnabled: false,
      
      // 层级选择
      layerSelection: '1',
      layerShowList: ['0', '1', '2', '3', '4']
    }
  },
  computed: {    
    // 获取打开地图的名称
    openedMapName () {
      const map = this.getMapById(this.openedId)
      if (map) {
        return map.name
      }
      return ''
    },
    
    // 获取选中地图的名称
    selectedMapName () {
      const map = this.getMapById(this.selectedId)
      if (map) {
        return map.name
      }
      return ''
    },
    
    // 地图容器的样式
    mapStyle () {
      if (this.mapData) {
        const padding = 10
        return {
          width: parseInt(this.mapData.colNum) * this.itemDefaultSize + 20 + 'px',
          height: parseInt(this.mapData.rowNum) * this.itemDefaultSize + 20 + 'px'
        }
      }
      return {}
    },
    
    // 鼠标覆盖区域的行列数
    hoverPos () {
      if (typeof this.hoverIndex !== 'number' || !this.mapData) {
        return null
      }
      
      return this.solveIndex(this.hoverIndex)
    },
    
    // 是否显示选中素材
    isSelectedObjShow () {
      const state = this.checkEditorState()
      if (state === 'noEdit' || state === 'erase') {
        // 未选中任何素材,或者橡皮擦模式下,不显示
        return false
      }
      return true
    },
    
    // 选中素材的行列数
    selectedRange () {
      if (!this.selectedObj || !this.hoverPos) {
        return { rowNum: 0, colNum: 0 }
      }
      
      const desColNum = this.hoverPos.colIndex + this.selectedObj.colNum > this.mapData.colNum ?
        this.mapData.colNum - this.hoverPos.colIndex : this.selectedObj.colNum
      const desRowNum = this.hoverPos.rowIndex + this.selectedObj.rowNum > this.mapData.rowNum ?
        this.mapData.rowNum - this.hoverPos.rowIndex : this.selectedObj.rowNum
      return { rowNum: desRowNum, colNum: desColNum }
    },
    
    // 显示的选中素材
    selectedTilesList () {
      if (!this.selectedObj || !this.hoverPos) {
        return []
      }
      
      let result = []
      let posResult = []
      const len = this.selectedObj.items.length
      for (let i = 0; i < len; i++) {
        const rowIndex = Math.floor(i / this.selectedObj.colNum)
        const colIndex = i % this.selectedObj.colNum
        if (rowIndex >= this.selectedRange.rowNum) {
          // 行数超出范围,立即跳出
          break
        }
        if (colIndex < this.selectedRange.colNum) {
          // 只选取范围内的图块
          // 记录大地图中的位置
          const posMap = {
            // 指定在地图中的行列
            mapRowIndex: this.hoverPos.rowIndex + rowIndex,
            mapColIndex: this.hoverPos.colIndex + colIndex
          }
          posResult.push(posMap)
          result.push(this.selectedObj.items[i])
        }
      }
      return {
        list: result,
        positions: posResult
      }
    },
    
    // 选中素材整体的样式
    selectedTilesStyle () {
      if (!this.hoverPos) {
        return {}
      }

      return {
        height: (this.selectedRange.rowNum * this.itemDefaultSize) + 'px',
        width: (this.selectedRange.colNum * this.itemDefaultSize) + 'px',
      }
    },
    
    // 图块个体的样式
    tileStyle () {
      return (item) => {
        let style = {
          top: 0,
          left: 0,
          zIndex: item.layer,
        }
        
        return style
      }
    },
    
    // 图片资源地址
    mapSrc () {
      return (item) => {
        return config.filePath + item.src
      }
    },
    
    // 决定图片是否显示
    isItemImgShow () {
      return (item, index) => {
        if (!item) {
          return false
        }
        
        const layer = index + ''
        
        return this.layerShowList.indexOf(layer) >= 0
      }
    },
    
    itemImgStyle () {
      return (item) => {
        if (item) {
          // 针对角色和其他还要修改
          return {
            zIndex: item.layer,
            top: 0,
            left: 0
          }          
        }
        return {}
      }
    }
  },
  methods: {
    // 更改重要数据时触发
    onChange (eventName = '') {
      // eventName 事件名称
      // preparations 地图预备资源库
      this.$emit('on-change', this.mapData, this.selectedItem, eventName)
    },
    
    // 获取将索引转化为行列信息
    solveIndex (index) {
      if (this.mapData) {
        const colNum = this.mapData.colNum
        const rowIndex = Math.floor(index / colNum)
        const colIndex = index % colNum
        
        return { rowIndex, colIndex }        
      }
      return null
    },
    
    // 打开地图
    openMap () {
      // console.log(this.mapSelected)
      if (!this.selectedId) {
        this.$Notice.error({
          title: '请在左边的地图列表中选择一张'
        })
        return
      }
      
      if (this.mapData && this.selectedId === this.mapData.id) {
        this.$Notice.error({
          title: '该地图已经打开'
        })
        return
      }
      
      this.isLoading = true
      request.getMapFile(this.selectedId).then((data) => {
        this.isLoading = false
        if (!data) {
          // 地图文件未创建
          this.$Notice.info({
            title: '地图文件不存在，需要创建'
          })
          this.createFileHandler()
        }
        else {
          this.$Notice.success({
            title: '地图加载成功'
          })
          this.mapData = data
          this.openedId = this.selectedId
          this.onChange('open')
        }
      })
      .catch((err) => {
        console.log(err)
        this.$Notice.error({
          title: '地图加载失败'
        })
        this.isLoading = false
      })
    },
    
    // 保存地图
    saveMap () {
      request.setMapDetail(this.openedId, this.mapData).then(() => {
        this.$Notice.success({
          title: '保存成功'
        })
        this.isSave = true
      })
      .catch((err) => {
        console.log(err)
        this.$Notice.error({
          title: '保存失败'
        })
      })
    },
    
    // 关闭地图
    closeMap () {
      this.mapData = null
      this.selectedItem = null
      this.openedId = null
      // 关地图触发
      this.onChange('close')
    },
    
    createFileHandler () {
      this.createModal = true
    },
    
    // 创建地图文件
    createMapFile () {
      this.$refs.createForm.validate((res) => {
        console.log(res)
        if (res) {
          this.createLoading = true
          request.addMapFile(this.selectedId, this.createForm).then((data) => {
            this.$Notice.success({
              title: '创建地图成功'
            })
            this.mapData = data
            this.openedId = this.selectedId
            this.onChange('open')
            this.createModal = false
            this.createLoading = false
          })
          .catch((err) => {
            console.log(err)
            this.$Notice.error({
              title: '创建地图出错'
            })
            this.createLoading = false
          })            
        }
      })
    },
    
    // Ctrl + C 复制事件
    copyHandler (index, data) {
      // 获取所在层
      const state = this.checkEditorState()
      const layer = parseInt(this.layerSelection)
      if (data.objects[layer] && state === 'noEdit') {
        this.copyItem = data.objects[layer]
        this.$Message.success({
          content: '成功复制对象'
        })
      }
    },
    
    // Ctrl + V 粘贴事件
    pasteHandler (index) {
      // 获取所在层
      const state = this.checkEditorState()
      const layer = parseInt(this.layerSelection)
      
      if (this.copyItem && state === 'noEdit') {
        // 如果不处在编辑模式,且复制区内有内容
        const data = utils.obj.copyObj(this.copyItem)
        this.$set(this.mapData.items[index].objects, layer, data)
      }
    },
    
    // 鼠标覆盖地图格子触发
    mouseenterHandler (index) {
      // 记录鼠标悬停坐标
      this.hoverIndex = index
      if (this.isDragEnabled) {
        this.fillMap(index)
      }
    },
    
    // 拖拽开始触发
    mousedownHandler (index) {
      this.isDragEnabled = true
      this.fillMap(index)
    },
    
    // 拖拽结束触发
    mouseupHandler () {
      this.isDragEnabled = false
      // console.log(this.mapData)
    },
    
    // 点击地图格子触发
    clickItemHandler (index) {
      // 选中格子
      this.selectedItem = index
      this.onChange('select')
    },
    
    // 取消选择
    selectNone () {
      this.selectedObj = null
    },
    
    // 检查编辑模式
    checkEditorState () {
      if (this.eraseEnabled) {
        // 先判定橡皮擦模式
        return 'erase'
      }
      
      if (!this.selectedObj) {
        // 未选中任何
        return 'noEdit'
      }
      
      const layer = parseInt(this.layerSelection)
      if (this.selectedObj.type === 'Env' && layer >= 0 && layer <= 2) {
        return 'editEnv'
      }
      if (this.selectedObj.type === 'Dynamic' && layer === 3) {
        return 'editDynamic'
      }
      if (this.selectedObj.type === 'Role' && layer === 4) {
        return 'editRole'
      }
      
      return 'noEdit'
    },
    
    // 点击层右边的眼镜触发
    showLayerHandler (layer) {
      const index = this.layerShowList.indexOf(layer)
      if (index < 0) {
        this.layerShowList.push(layer)
      }
      else {
        this.layerShowList.splice(index, 1)
      }
    },
    
    // 地图填充
    fillMap (index) {
      const state = this.checkEditorState()
      const layer = parseInt(this.layerSelection)
      if (state === 'noEdit') {
        // 不编辑模式
        return
      }
      
      if (state === 'erase') {
        // 橡皮擦模式
        if (this.layerShowList.indexOf(this.layerSelection) < 0) {
          this.$Notice.error({
            title: '不能在编辑层不可见的情况下擦除'
          })
        }
        else {
          this.erase(layer, index)
        }
        return
      }
      
      if (state === 'editEnv') {
        // 编辑模式
        if (this.selectedObj.isAutotile) {
          // 填充Autotile
          this.fillAutotile(this.selectedObj.items[0], layer, index)
        }
        else {
          // 填充瓦片
          this.fillTiles(layer, index)
        }
      }        
    },
    
    // 全场填充环境图块
    fillEnvAll () {
      const layer = parseInt(this.layerSelection)
      const state = this.checkEditorState()
      if (state === 'noEdit') {
        this.$Notice.error({
          title: '当前没有有效的填充素材'
        })
        return
      }
      
      // 只有环境才可以填充
      if (state !== 'erase' && state !== 'editEnv') {
        this.$Notice.error({
          title: '环境层之外的其它层不支持全场填充'
        })
        return
      }
      
      // Autotile不支持填充
      if (state === 'editEnv' && this.selectedObj.isAutotile) {
        this.$Notice.error({
          title: 'Autotile不支持全场填充'
        })
        return  
      }
      
      // 橡皮擦模式
      if (state === 'erase' && this.layerShowList.indexOf(this.layerSelection) < 0) {
        this.$Notice.error({
          title: '不能在编辑层不可见的情况下擦除'
        })
        return
      }
      
      const colNum = this.mapData.colNum
      const rowNum = this.mapData.rowNum
      for (let i = 0; i < rowNum; i++) {
        for (let j = 0; j < colNum; j++) {
          // 填充瓦片,以选中范围的第一格填充
          const index = i * colNum + j
          if (state === 'erase') {
            this.erase(layer, index)
          }
          else {
            this.$set(this.mapData.items[index].objects, layer, utils.obj.copyObj(this.selectedObj.items[0]))
          }
        }
      }
      
      if (state !== 'erase') {
        // 触发数据更新
        this.onChange('set')
      }
    },
    
    // 擦除
    erase (layer, index) {      
      let item = this.mapData.items[index].objects[layer]
      if (!item) {
        return
      }
      
      // 擦除Autotile
      if (item.autotile) {
        // 临时保存数据
        let tempItem = utils.obj.copyObj(item)
        // 先把当前块给擦除
        this.$set(this.mapData.items[index].objects, layer, null)
        // 改变四面八方的Autotile
        const directions = this.getDirections(index)
        const autotileStr = this.getDirectionsAutotile(item, directions, index, layer)
        // 遍历八方的Autotile是否需要改变
        for (let i = 0; i < 8; i++) {
          if (directions[i] !== null && autotileStr[i] === '1') {
            // 对应位置可通行,且具有相同的Autotile
            this.fillAutotile(item, layer, directions[i], false)
          }
        }
      }
      else {
        this.$set(this.mapData.items[index].objects, layer, null)
      }
      // 触发数据更新
      this.onChange('set')
    },
    
    // 填充Autotile
    fillAutotile (item, layer, index, isSurroundCheck = true) {
      const rules = autotilesOptions.rules
      // 拷贝一份,省得出事改变了其他元素
      const directions = this.getDirections(index)
      const autotileStr = this.getDirectionsAutotile(item, directions, index, layer)
      const ruleObj = rules.find(rule => rule.match(autotileStr))
      
      if (ruleObj) {
        // 根据匹配到的规则,新获取变形后的Autotile对象
        let autotile = this.changeAutotile(item, ruleObj)
        // 当前格子匹配对应的Autotile
        this.$set(this.mapData.items[index].objects, layer, autotile)
        // console.log(this.mapData.items[index].objects[state])
        if (isSurroundCheck) {
          // 遍历八方的Autotile是否需要改变
          for (let i = 0; i < 8; i++) {
            if (directions[i] !== null && autotileStr[i] === '1') {
              // 对应位置可通行,且具有相同的Autotile
              this.fillAutotile(item, layer, directions[i], false)
            }
          }
        }
      }
      // 触发数据更新
      this.onChange('set')
    },
    
    // 填充普通图块
    fillTiles (layer, index) {
      const selectedTiles = this.selectedTilesList
      const len = selectedTiles.list.length
      for (let i = 0; i < len; i++) {
        const index = selectedTiles.positions[i].mapRowIndex * this.mapData.colNum + selectedTiles.positions[i].mapColIndex
        this.$set(this.mapData.items[index].objects, layer, utils.obj.copyObj(this.selectedTilesList.list[i]))
      }
      // 触发数据更新
      this.onChange('set')
    },
    
    // 获取某侧区块,direction的0/1/2/3/4/5/6/7 分别对应 左/上/右/下/左上/右上/左下/右下
    getDirectionItem (index, direction) {
      const indexMap = this.solveIndex(index)
      let row = indexMap.rowIndex
      let col = indexMap.colIndex
      
      const colNum = this.mapData.colNum
      const rowNum = this.mapData.rowNum
      switch (direction) {
        case 0:
          col--
          break
        case 1:
          row--
          break
        case 2:
          col++
          break
        case 3:
          row++
          break
        case 4:
          col--
          row--
          break
        case 5:
          col++
          row--
          break
        case 6:
          col--
          row++
          break
        default:
          col++
          row++
      }
      
      if (row < 0 || row >= rowNum || col < 0 || col >= colNum) {
        return null
      }
      return row * colNum + col
    },
    
    // 直接获取四面八方的区块
    getDirections (index) {
      let result = []
      for (let i = 0; i < 8; i++) {
        const num = this.getDirectionItem(index, i)
        result.push(num)
      }
      return result
    },
    
    // 返回八方有没有Autotile情况,返回八位二进制字符串
    getDirectionsAutotile (item, directions, index, layer) {
      let result = ''
      const indexAutotile = item.autotile
      for (let i = 0; i < 8; i++) {
        if (directions[i] === null) {
          // 边界邻接,视作有
          result = result + '1'
        }
        else {
          // 否则,对应层一定要有,才为true
          let item = this.mapData.items[directions[i]]
          if (item.objects[layer] && item.objects[layer].autotile && item.objects[layer].autotile === indexAutotile) {
            result = result + '1'
          }
          else {
            result = result + '0'
          }
        }
      }
      return result
    },
    
    // 对Autotile进行变形,获取变形后的Autotile对象
    changeAutotile (item, rule) {
      // 先复制对象,省得以后出事
      let autotile = utils.obj.copyObj(item)
      // 先对资源地址src进行改变
      autotile.src = this.changeAutotileSrc(autotile.src, rule, autotile.isFrames)
      
      // 再对动画资源地址进行改变
      if (autotile.isFrames) {
        let srcList = autotile.animations.default.srcList
        const len = srcList.length
        for (let i = 0; i < len; i++) {
          srcList[i] = this.changeAutotileSrc(srcList[i], rule, autotile.isFrames)
        }        
      }

      return autotile
    },
    
    // 改变Autotile资源地址
    changeAutotileSrc (src, rule, isFrames) {
      // 获取后缀
      const tail = utils.str.getFileTail(src)
      // 去除后缀
      src = utils.str.cutFileTail(src)
      // 下划线分割
      let srcArr = src.split('_')
      const len = srcArr.length
      // 倒数第一个为数字
      if (isFrames) {
        // 对于有动画的Autotile,倒数第二位需要变化
        srcArr[len - 2] = rule.name
      }
      else {
        // 否则变化最后一位
        srcArr[len - 1] = rule.name
      }
      return srcArr.join('_') + '.' + tail
    },
    
    // 递归获取地图对象
    getMapById (id) {
      if (!id) {
        return null
      }
      
      const len = this.mapFolder.length
      
      for (let i = 0; i < len; i++) {
        let result = this.findMapById(this.mapFolder[i], id)
        if (result) {
          return result
        }
      }
      
      return null
    },
    
    // 递归函数找map
    findMapById (obj, id) {
      if (obj.id === id) {
        return obj
      }
      
      if (obj.children) {
        const len = obj.children.length
        for (let i = 0; i < len; i++) {
          let result = this.findMapById(obj.children[i], id)
          if (result) {
            return result
          }
        }
      }
      
      return null
    },
    
    // 保存状态取消
    notSave () {
      this.isSave = false
    },
    
    // 保存状态初始化
    initSave () {
      this.isSave = true
    },
  },
  created () {
    
  },
  watch: {
    map: {
      handler (newVal, oldVal) {
        this.mapData = newVal
      },
      deep: true
    },
    selectedTiles: {
      handler (newVal, oldVal) {
        this.selectedObj = newVal
      },
      deep: true
    },
  }
}